package com.baizhi.shane.sys.service.impl;

import com.baizhi.shane.constant.SysMapping;
import com.baizhi.shane.sys.dto.UserRoleEnNameListDTO;
import com.baizhi.shane.sys.entity.SysRole;
import com.baizhi.shane.sys.entity.SysUserRoleRel;
import com.baizhi.shane.sys.enums.SysRoleEnum;
import com.baizhi.shane.sys.manager.RedisManager;
import com.baizhi.shane.sys.service.SysRoleService;
import com.baizhi.shane.sys.service.SysUserRoleRelService;
import com.baizhi.shane.sys.utils.CommonUtils;
import com.baizhi.shane.sys.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baizhi.shane.sys.entity.SysUser;
import com.baizhi.shane.sys.service.SysUserService;
import com.baizhi.shane.sys.mapper.SysUserMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author wbz12
 * @description 针对表【sys_user(系统用户表)】的数据库操作Service实现
 * @createDate 2023-03-26 18:19:03
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
        implements SysUserService {

    @Resource
    private CommonUtils commonUtils;
    @Resource
    private RedisManager redisManager;
    @Resource
    private SysUserRoleRelService sysUserRoleRelService;
    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private HttpServletRequest request;

    /**
     * 注册
     *
     * @return boolean
     * @Author 闻柏智
     * @Date 21:56 2023/3/21
     * @Param userSignUpVO
     **/
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public UserSignUpOutVO signUp(UserSignUpInVO userSignUpInVO) {
        /* 提取变量 */
        String username = userSignUpInVO.getUsername();
        String password = userSignUpInVO.getPassword();
        String email = userSignUpInVO.getEmail();
        String realname = userSignUpInVO.getRealname();
        Long divisionId = userSignUpInVO.getDivisionId();
        String divisionCode = userSignUpInVO.getDivisionCode();
        Integer status = userSignUpInVO.getStatus();
        /* 入参异常处理 */
        if (StringUtils.isAnyEmpty(username, password)) {
            throw new RuntimeException("用户名和密码不能为空");
        }
        // 验证用户名是否已在库中存在
        boolean usernameAlreadyExists = this.checkUsernameAlreadyExists(username);
        /* 已存在则不允许注册，抛异常 */
        if (usernameAlreadyExists) {
            throw new RuntimeException("用户名已存在");
        }
        // 生成密码盐
        String salt = UUID.randomUUID().toString().replace("-", "");
        /* 生成密码md5值 */
        String passwordAndSalt = password + salt;
        String passwordMd5 = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());
        // 获取当前时间
        Date creatTime = new Date();
        /* 构建用户对象 */
        SysUser sysUser = new SysUser();
        sysUser.setUsername(username);
        sysUser.setPasswordMd5(passwordMd5);
        sysUser.setSalt(salt);
        sysUser.setEmail(email);
        sysUser.setRealname(realname);

        sysUser.setDivisionId(divisionId);
        sysUser.setDivisionCode(divisionCode);
        sysUser.setStatus(status);
        sysUser.setCreateTime(creatTime);
        // 插入用户
        boolean success = this.save(sysUser);
        if (!success) {
            throw new RuntimeException("插入新用户失败");
        }
        /* 插入角色关系 */
        LambdaQueryWrapper<SysRole> qwsr = new LambdaQueryWrapper<>();
        qwsr.eq(SysRole::getRoleCode, SysRoleEnum.USER.getRoleCode());
        Long roleId = sysRoleService.getOne(qwsr).getId();
        SysUserRoleRel sysUserRoleRel = new SysUserRoleRel();
        sysUserRoleRel.setUserId(sysUser.getId());
        sysUserRoleRel.setRoleId(roleId);
        sysUserRoleRelService.save(sysUserRoleRel);
        /* 构建返回值 */
        UserSignUpOutVO userSignUpOutVO = new UserSignUpOutVO();
        userSignUpOutVO.setId(String.valueOf(sysUser.getId()));
        userSignUpOutVO.setCreatTime(creatTime);


        return userSignUpOutVO;
    }

    /**
     * 注册
     *
     * @return UserLoginOutVO
     * @Author 闻柏智
     * @Date 21:56 2023/3/21
     * @Param userLoginInVO
     **/
    @Override
    public UserLoginOutVO login(UserLoginInVO userLoginInVO) {
        /* 提取变量 */
        String username = userLoginInVO.getUsername();
        String password = userLoginInVO.getPassword();
        /* 异常处理 */
        if (StringUtils.isAnyEmpty(username, password)) {
            throw new RuntimeException("入参不能为空");
        }
        // 验证用户是否存在
        boolean isUsernameAlreadyExists = this.checkUsernameAlreadyExists(username);
        if (!isUsernameAlreadyExists) {
            throw new RuntimeException("用户名不存在");
        }
        /* 获取密码盐、密码MD5值 */
        LambdaQueryWrapper<SysUser> qwSu = new LambdaQueryWrapper<>();
        qwSu.select(SysUser::getId, SysUser::getPasswordMd5, SysUser::getSalt);
        qwSu.eq(SysUser::getUsername, username);
        SysUser sysUser = this.getOne(qwSu);
        String salt = sysUser.getSalt();
        String passwordMd5Origin = sysUser.getPasswordMd5();
        /* 计算入参密码+盐的MD5值 */
        String passwordAndSalt = password + salt;
        String passwordMd5Input = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());
        /* 密码不正确则抛出异常 */
        if (!StringUtils.equals(passwordMd5Input, passwordMd5Origin)) {
            throw new RuntimeException("密码错误");
        }
        /* 获取用户的角色集合 */
        List<UserRoleEnNameListDTO> userUserRoleEnNameDTOList =
                this.getBaseMapper().getUserRoleEnNameList(sysUser.getId());

        // 生成uuid作为Token
        String token = UUID.randomUUID().toString().replace("-", "");
        /* token与userId对应关系存入Redis */
        Long userId = sysUser.getId();
        final long OneDay = 60L * 60 * 24;
        boolean setRedisSuccess = redisManager.set(token, userId, OneDay);
        if (!setRedisSuccess) {
            throw new RuntimeException("插入Redis失败");
        }
        /* 构建返回值 */
        UserLoginOutVO userLoginOutVO = new UserLoginOutVO();
        userLoginOutVO.setToken(token);

        return userLoginOutVO;
    }

    /**
     * 更新用户登录的缓存信息
     *
     * @Author 闻柏智
     * @Date 12:39 2023/12/31
     * @Param token
     * @Param userId: 默认为当前登录用户
     * @Param time: 默认为1天
     * @return boolean
     **/
    @Override
    public boolean updateUserLoginCache(String token, Long userId, Long time) {
        if (userId == null) {
            userId = redisManager.getUserId();
        }
        if (time == null) {
            time = 60L * 60 * 24;
        }
        boolean setRedisSuccess = redisManager.set(token, userId, time);
        return setRedisSuccess;
    }

    /**
     * 获取当前登陆人信息
     *
     * @return com.baizhi.shane.sys.vo.LoginUserVO
     * @Author 吴若琦
     * @Date 16:21 2023/7/29
     * @Param
     **/
    public LoginUserVO getLoginUserInfo() {
        String token = request.getHeader(SysMapping.TOKEN);
        Long userId = (Long) redisManager.get(token);
        LambdaQueryWrapper<SysUser> qwSu = new LambdaQueryWrapper<>();
        qwSu.eq(SysUser::getId, userId);
        SysUser sysUser = this.getOne(qwSu);
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setUserId(userId);
        BeanUtils.copyProperties(sysUser, loginUserVO);
        List<String> userRoleEnNameList = this.getUserRoleEnNameList(userId);
        /* 对列表进行去重 */
        userRoleEnNameList = userRoleEnNameList
                .stream()
                .distinct()
                .collect(Collectors.toList());
        loginUserVO.setRoleNameEnList(userRoleEnNameList);

        return loginUserVO;
    }

    /**
     * 通过userId获取角色英文名列表
     *
     * @return java.util.List<java.lang.String>
     * @Author 吴若琦
     * @Date 16:21 2023/7/29
     * @Param userId
     **/
    public List<String> getUserRoleEnNameList(Long userId) {
        List<UserRoleEnNameListDTO> userRoleEnNameList =
                this.getBaseMapper().getUserRoleEnNameList(userId);
        List<String> roleNameList = userRoleEnNameList
                .stream()
                .map(UserRoleEnNameListDTO::getRoleNameEn)
                .collect(Collectors.toList());
        return roleNameList;
    }

    /**
     * 用户退出登录
     *
     * @return java.lang.String
     * @Author 闻柏智
     * @Date 11:09 2023/10/25
     * @Param
     **/
    @Override
    public String loginOut() {
        String token = request.getHeader(SysMapping.TOKEN);
        redisManager.remove(token);
        return SysMapping.LOGIN_OUT_SUCCESS;
    }

    /**
     * 用户修改用户名
     *
     * @return java.lang.String
     * @Author 闻柏智
     * @Date 23:56 2023/10/29
     * @Param updateUsernameVO
     **/
    @Override
    public String updateUsername(UpdateUsernameVO updateUsernameVO) {
        String newUsername = updateUsernameVO.getNewUsername();
        Long userId = redisManager.getUserId();
        LambdaUpdateWrapper<SysUser> uwSu = new LambdaUpdateWrapper<>();
        uwSu.set(SysUser::getUsername, newUsername);
        uwSu.eq(SysUser::getId, userId);
        boolean success = this.update(uwSu);
        String successStr = commonUtils.boolToString(success, SysMapping.UPDATE_USERNAME_SUCCESS, SysMapping.UPDATE_USERNAME_FAIL);
        return successStr;
    }

    /**
     * 用户修改密码
     *
     * @return java.lang.String
     * @Author 闻柏智
     * @Date 0:07 2023/10/30
     * @Param updateUsernameVO
     **/
    @Override
    public SuccessedOutVO updatePassword(UpdatePasswordVO updateUsernameVO) {
        String newPassword = updateUsernameVO.getNewPassword();
        // 生成密码盐
        String salt = UUID.randomUUID().toString().replace("-", "");
        /* 生成密码md5值 */
        String passwordAndSalt = newPassword + salt;
        String passwordMd5 = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());

        Long userId = redisManager.getUserId();

        LambdaUpdateWrapper<SysUser> uwSu = new LambdaUpdateWrapper<>();
        uwSu.set(SysUser::getSalt, salt);
        uwSu.set(SysUser::getPasswordMd5, passwordMd5);
        uwSu.eq(SysUser::getId, userId);

        boolean success = this.update(uwSu);
        String message = commonUtils.boolToString(success, SysMapping.UPDATE_PASSWORD_SUCCESS, SysMapping.UPDATE_PASSWORD_FAIL);
        String successed = commonUtils.boolToString(success, SysMapping.NUMBER_1, SysMapping.NUMBER_0);

        SuccessedOutVO successedOutVO = new SuccessedOutVO();
        successedOutVO.setMessage(message);
        successedOutVO.setSuccessed(successed);
        return successedOutVO;
    }

    /**
     * 验证传入的字符串是否是用户的真实密码
     * 为真返回1，为假返回0
     *
     * @Author 闻柏智
     * @Date 23:53 2023/12/28
     * @Param checkUserPasswordInVO
     * @return java.lang.String
     **/
    @Override
    public String checkUserPassword(CheckUserPasswordInVO checkUserPasswordInVO) {
        String result = SysMapping.NUMBER_0;
        /* 提取变量 */
        Long userId = checkUserPasswordInVO.getUserId();
        /* 若用户ID为空则为当前登录用户 */
        if (userId == null) {
            userId = redisManager.getUserId();
        }
        String password = checkUserPasswordInVO.getPassword();
        // 查出用户
        SysUser sysUser = this.getById(userId);
        // 提取密码盐
        String salt = sysUser.getSalt();
        // 提取密码MD5值
        String passwordMd5 = sysUser.getPasswordMd5();
        /* 验证密码是否正确 */
        // 计算前端传入的密码的MD5值
        String checkStr = password + salt;
        String checkMd5 = DigestUtils.md5DigestAsHex(checkStr.getBytes());
        if (StringUtils.equals(passwordMd5,checkMd5)) {
            result = SysMapping.NUMBER_1;
        }
        return result;
    }

    /**
     * 修改用户信息
     *
     * @Author 闻柏智
     * @Date 18:36 2023/12/29
     * @Param sysUser
     * @return com.baizhi.shane.sys.vo.SuccessedOutVO
     **/
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public SuccessedOutVO updateUserInfo(SysUser sysUser) {
        boolean success = this.updateById(sysUser);
        SuccessedOutVO successedOutVO = commonUtils.boolToSuccessedOutVO(success, SysMapping.CHANGE_SUCCESS, SysMapping.CHANGE_FAIL);
        return successedOutVO;
    }

    @Override
    public SuccessedOutVO checkNowLoginUserHasAdminRole() {
        LoginUserVO loginUserInfo = this.getLoginUserInfo();
        List<String> roleNameEnList = loginUserInfo.getRoleNameEnList();
        boolean success = roleNameEnList.contains(SysRoleEnum.ADMIN.getNameEn());
        SuccessedOutVO successedOutVO = commonUtils.boolToSuccessedOutVO(success, SysMapping.HAS_ADMIN_ROLE, SysMapping.NOT_HAS_ADMIN_ROLE);
        return successedOutVO;
    }

    /**
     * 删除用户-通过ID
     *
     * @Author 闻柏智
     * @Date 17:55 2024/3/4
     * @Param idVO
     * @return com.baizhi.shane.sys.vo.SuccessedOutVO
     **/
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public SuccessedOutVO deleteOneUserById(IdVO idVO) {
        // 删除用户
        boolean deleteUserSuccessed = this.removeById(idVO.getId());
        /* 删除用户的角色 */
        LambdaQueryWrapper<SysUserRoleRel> qwSurr = new LambdaQueryWrapper<>();
        qwSurr.eq(SysUserRoleRel::getUserId, idVO.getId());
        boolean deleteRoleRelSuccessed = sysUserRoleRelService.remove(qwSurr);
        // 删除全成功则为删除成功
        boolean successed = deleteUserSuccessed && deleteRoleRelSuccessed;
        // 构造返回值
        SuccessedOutVO successedOutVO = commonUtils.boolToSuccessedOutVO(successed, null, null);
        return successedOutVO;
    }

    /**
     * 检验用户名是否已在数据库中存在，存在返回true，不存在返回False
     *
     * @param username
     * @return
     */
    public boolean checkUsernameAlreadyExists(String username) {
        LambdaQueryWrapper<SysUser> qwSu = new LambdaQueryWrapper<>();
        qwSu.eq(SysUser::getUsername, username);
        int count = this.count(qwSu);
        boolean isExists = commonUtils.countToBoolean(count);
        return isExists;
    }

    /**
     * 密码字符串转MD5
     *
     * @return java.lang.String
     * @Author 闻柏智
     * @Date 20:43 2023/3/26
     * @Param password
     **/
    @Override
    public String passwordToPasswordMd5(String password, String salt) {
        /* 生成密码md5值 */
        String passwordAndSalt = password + salt;
        String passwordMd5 = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());
        return passwordMd5;
    }

    @Override
    public String getSalt() {
        String salt = UUID.randomUUID().toString().replace("-", "");
        return salt;
    }


}




